package fast.loader;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import fast.data.BaseStats;
import fast.data.CharacterClass;
import fast.data.Spec;
import fast.data.WeaponType;
import fast.mvc.View;

public class FastLoader {
	
	private HashMap<String,Integer> items = new HashMap<String,Integer>();
	BaseStats base = new BaseStats();
	EnchantDB enchantDB = new EnchantDB();
	
	Equipment equipment = new Equipment();
	
	ItemDB itemDB;
	GemDB gemDB;
	
	public final static ItemSource itemSource = ItemSource.wowhead;
	public final static String wowheadPrefix = "ptr";
	private boolean reImport = false;
	
//	public static void main(String[] args){
//		ItemDB itemDB = new ItemDB();
//		FastLoader loader = new FastLoader();
//		Set<Integer> ids = loader.getIdsFromWowhead("http://ptr.wowhead.com/search?q=ticking+ebon");
//		ids.addAll(loader.getIdsFromWowhead("http://ptr.wowhead.com/search?q=assurance+of"));
//		ids.addAll(loader.getIdsFromWowhead("http://ptr.wowhead.com/search?q=haromms+talisman"));
//		ids.addAll(loader.getIdsFromWowhead("http://ptr.wowhead.com/search?q=sigil+of+rampage"));
//		for(Integer id : ids){
//			Item item = itemDB.getItemFromWOWHEAD(id);
//			System.out.println(item.getId() + " " + item.getName() + " " + item.getTag());
//		}
//	}
	
	public void load2(String playername,String server,String region) throws JSONException,CharacterNotFoundException {
		
		base = new BaseStats();
		equipment = new Equipment();
		
		server = server.replaceAll(" ", "%20");
		String json = loadURL(region,server,playername);
		JSONObject js = null;
		File dbDir = new File("./db/");
		if(!dbDir.exists())
			dbDir.mkdir();
		File file = new File("./db/" + region.toLowerCase() + "-" + server.toLowerCase()+"-"+playername.toLowerCase()+".character");
		try {
			js = new JSONObject(json);
		} catch (JSONException e) {
			e.printStackTrace();
		}
		
		try {
			PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(file)));
			out.write(json);
			out.flush();
			out.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		JSONObject items = (JSONObject) js.get("items");
		for ( Iterator<?> i = items.keys(); i.hasNext(); )
		{
			String s = (String) i.next();
			Object item = items.get(s);
			if(item instanceof Integer){
				
			}
			else {
				int id = ((JSONObject)item).getInt("id");
				this.items.put(s, id);
			}
		}
		for( String name: this.items.keySet() )
	    {
			int itemid = this.items.get(name);
			Item baseItem = itemDB.get(itemid);
			if(baseItem == null)
				baseItem = itemDB.getItemFromSource(itemid);
			ItemEquipped item = new ItemEquipped();
			item.setItem(baseItem);
			
			// enchant etc.
			JSONObject iteme = new JSONObject();
			if(items.has(name))
				iteme = (JSONObject) items.get(name);
			
			JSONObject tooltip = null;
			Enchant e = null;
			if(iteme.has("tooltipParams")){
				tooltip = (JSONObject) iteme.get("tooltipParams");
				if(tooltip.has("upgrade")){
					JSONObject upgrade = (JSONObject) tooltip.get("upgrade");
					if(upgrade.has("current")){
						int upgrade_level = upgrade.getInt("current");
						item.setItem(itemDB.get(itemid, upgrade_level));
					}
				}
				if(tooltip.has("enchant")){
					int enchantid = tooltip.getInt("enchant");
					e = enchantDB.getEnchant(enchantid);
					if(e != null){
						item.setEnchant(e);
					}
				} else {
					item.setEnchant(null);
				}
				if(tooltip.has("extraSocket") && tooltip.getBoolean("extraSocket")){
					int i = item.getItem().getNumSockets();
					item.getItem().gemSlot[i] = Socket.prismatic;
				}else{
					System.out.println(itemid);
					System.out.println(item.getItem());
					int i = item.getItem().getNumSockets();
					if(i < item.getItem().gemSlot.length){
						item.getItem().gemSlot[i] = null;
					}
				}
				if(tooltip.has("gem0")){
	    		   int gemid = tooltip.getInt("gem0");
	    		   Gem g = gemDB.getGem(gemid);
	    		   item.setGem(0, g);
				}else {
					item.setGem(0, null);
				}
				if(tooltip.has("gem1")){
	    		   int gemid = tooltip.getInt("gem1");
	    		   Gem g = gemDB.getGem(gemid);
	    		   item.setGem(1, g);
				}else {
					item.setGem(1, null);
				}
				if(tooltip.has("gem2")){
	    		   int gemid = tooltip.getInt("gem2");
	    		   Gem g = gemDB.getGem(gemid);
	    		   item.setGem(2, g);
				}else {
					item.setGem(2, null);
				}
				if(tooltip.has("reforge")){
					int reforgeid = tooltip.getInt("reforge");
					Reforge r = ReforgeDB.getReforge(reforgeid);
					item.setReforge(r);
				} else {
					item.setReforge(null);
				}
				if(tooltip.has("suffix")){
					int suffixid = Math.abs(tooltip.getInt("suffix"));
					item.setItemSuffix(suffixid);
				}
			}
	       
			if(item.getItem().getDps() > 0){
				WeaponType type = null;
				for(WeaponType wtype : WeaponType.values()){
					if(wtype.getIndex() == item.getItem().getItemSubClass()){
						type = wtype;
					}
				}
				String enchant = "";
				if(e != null){
					enchant = e.getName(); 
				}
				if(name.equals("mainHand")){ // mainHand
					base.setMH(new Weapon(item.getItem().getSpeed(),item.getItem().getDps(),type,enchant));
				}else if(name.equals("offHand")){ // offHand
					base.setOH(new Weapon(item.getItem().getSpeed(),item.getItem().getDps(),type,enchant));
				}
			}
			
			if(item.getItem().getGearBuff() != null)
				base.addGearBuff(item.getItem().getGearBuff());
			
			equipment.put(Slot.valueOf(name), item);
			this.itemDB.put(item.getItem().getId(), baseItem);
	    }
		
		// Class
		base.setCharacterClass(CharacterClass.values()[js.getInt("class")-1]);
		
		// Race
		int raceid = js.getInt("race");
		if(Race.pandarenIds.contains(raceid))
			base.setRace(Race.pandaren);
		for(Race race : Race.values()){
			if(race.getIndex() == raceid){
				base.setRace(race);
				break;
			}
		}
		
		// Professions
		JSONObject profession = js.getJSONObject("professions");
		JSONArray professions = profession.getJSONArray("primary");
		for(int i = 0;i < professions.length(); i++) {
			JSONObject prof = (JSONObject) professions.get(i);
			base.addProfession(Profession.valueOf(prof.getString("name")));
		}
		
		GlyphDB gDB = new GlyphDB();
		HashMap<String, Integer> specMap = new HashMap<String,Integer>();
		specMap.put("a", 0); specMap.put("Z", 1); specMap.put("b", 2); specMap.put("Y", 3);
		// talents, glyphs
		JSONArray talents = (JSONArray) js.get("talents");
		for(int i = 0;i < talents.length(); i++) {
			JSONObject spec = (JSONObject) talents.get(i);
			Spec[] classSpecs = Spec.getSpecsByCharacterClass(base.getCharacterClass());
			String calcSpec = spec.getString("calcSpec");
			List<String> glyphnames = new ArrayList<String>();
			for(String glyphcat : JSONObject.getNames(spec.getJSONObject("glyphs"))){
				JSONArray glyphs = spec.getJSONObject("glyphs").getJSONArray(glyphcat);
				for(int g = 0; g < glyphs.length(); g++){
					glyphnames.add(gDB.getGlyph(glyphs.getJSONObject(g).getInt("glyph")).getName());
				}
			}
			
			if((spec.has("selected") && spec.getBoolean("selected"))){
				if(spec.has("spec")){
					this.base.setSpec(classSpecs[specMap.get(calcSpec)]);
					this.base.setTalents(spec.getString("calcTalent"));
					this.base.setGlyphs(glyphnames);
				} else {
					this.base.setSpec(Spec.Combat);
				}
			}else{
				if(spec.has("spec")){
					this.base.setOffSpec(classSpecs[specMap.get(calcSpec)]);
					this.base.setOffTalents(spec.getString("calcTalent"));
					this.base.setOffGlyphs(glyphnames);
				} else {
					this.base.setOffSpec(Spec.Subtlety);
				}
			}
		}
		
		// achievements
		// "achievements":{"achievementsCompleted"
		JSONObject achievements = js.getJSONObject("achievements");
		JSONArray achievementsCompleted = achievements.getJSONArray("achievementsCompleted");
		for(int i = 0; i < achievementsCompleted.length(); i++){
			//System.out.println(achievementsCompleted.getInt(i));
			base.getAchievements().add(achievementsCompleted.getInt(i));
		}
		
		System.out.println(js.getString("name") + " - Level " + js.getInt("level"));
		
//		for( String name: this.base.getSetItems() ) {
//			System.out.println(name + ": piece/s");
//	    }
//		for( String name: this.base.getGearBuffs() ) {
//			System.out.print(name + ", ");
//	    }
//		System.out.println();
	}
	
	public FastLoader() {
		base = new BaseStats();
		equipment = new Equipment();
		gemDB = new GemDB(this);
		
		if(reImport){
			populateGems("http://"+wowheadPrefix+".wowhead.com/items=3?filter=qu=2:3;minle=86;maxle=90;eb=1");
			populateGems("http://"+wowheadPrefix+".wowhead.com/items=3?filter=qu=2:3:4;minle=86;maxle=90;cr=99;crs=11;crv=0");
			gemDB.getGem(89873); // legendary weapon gem
			gemDB.getGem(95346); // legendary meta gem
		}
		//http://#{prefix}.wowhead.com/items?filter=qu=4;minle=346;maxle=358;ub=4;cr=21;crs=1;crv=0
		
		// new stuff
		//new TalentDB();
	}

	private String loadURL(String region, String server, String playername) throws CharacterNotFoundException {
		
		BufferedReader in;
		try {
			File file = new File("./db/" + region.toLowerCase() + "-" + server.toLowerCase()+"-"+playername.toLowerCase()+".character");
			in = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF-8"));  
			String content = in.readLine();
			in.close();
			return content;
		} catch (IOException es) {
			//throw new CharacterNotFoundException("Character not found");
		}
		String url = "http://"+region+".battle.net/api/wow/character/"+server+"/"+playername+"?fields=items,talents,stats,professions,achievements";
		InputStream is = null;
		BufferedReader rd = null;
		try {
			is = new URL(url).openStream();
			rd = new BufferedReader(new InputStreamReader(is, "UTF-8"));
		} catch (FileNotFoundException e) {
			throw new CharacterNotFoundException("Character not found");
		} catch (MalformedURLException e) {
			e.printStackTrace();
		} catch (IOException e) {
			View.addConsole("no internet connection, try to read from hdd");
			//e.printStackTrace();
			//BufferedReader in;
			try {
				File file = new File("./db/" + region.toLowerCase() + "-" + server.toLowerCase()+"-"+playername.toLowerCase()+".character");
				in = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF-8"));  
				String content = in.readLine();
				in.close();
				return content;
			} catch (IOException es) {
				throw new CharacterNotFoundException("Character not found");
			}
		}
		String cp = "";
		StringBuilder sb = new StringBuilder();
		try {
			while ((cp = rd.readLine()) != null) {
			      sb.append(cp);
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		try {
			is.close();
			rd.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return sb.toString();
	}
	
	public static String loadURL(String url) {
		InputStream is = null;
		BufferedReader rd = null;
		try {
			is = new URL(url).openStream();
			rd = new BufferedReader(new InputStreamReader(is, Charset.forName("UTF-8")));
		} catch (MalformedURLException e) {
			e.printStackTrace();
		} catch (IOException e) {
			System.out.println("not found: " + url);
			//e.printStackTrace();
			return "{\"status\":\"nok\", \"reason\": \"unable to get item information.\"}";
		}
		String cp = "";
		StringBuilder sb = new StringBuilder();
		try {
			while ((cp = rd.readLine()) != null) {
			      sb.append(cp);
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		try {
			is.close();
			rd.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return sb.toString();
	}
	
	public BaseStats getBase(){
		return base;
	}

	public Equipment getEquipment() {
		return equipment;
	}
	
	public void setDatabase(ItemDB db){
		this.itemDB = db;
		
		/*populateItems("http://"+wowheadPrefix+".wowhead.com/items?filter=qu=4;minle=346;maxle=358;ub=4;cr=21;crs=1;crv=0");
		populateItems("http://"+wowheadPrefix+".wowhead.com/items?filter=qu=4;minle=359;maxle=371;ub=4;cr=21;crs=1;crv=0");
		populateItems("http://"+wowheadPrefix+".wowhead.com/items?filter=qu=4;minle=372;maxle=410;ub=4;cr=21;crs=1;crv=0");*/
		
		//populateItems("http://"+wowheadPrefix+".wowhead.com/items?filter=qu=3;minle=346;ub=4;cr=21;crs=1;crv=0");
		
		//populateItems("http://"+wowheadPrefix+".wowhead.com/items?filter=qu=4;minle=359;cr=13;crs=1;crv=0");
		
		itemDB.get(98148);
		
		if(reImport){
			Set<Integer> items = new HashSet<Integer>();
			items.addAll(getIdsFromWowhead("http://"+wowheadPrefix+".wowhead.com/items?filter=qu=4;minle=430;maxle=483;ub=4;cr=21;crs=1;crv=0"));
			items.addAll(getIdsFromWowhead("http://"+wowheadPrefix+".wowhead.com/items?filter=qu=4;minle=484;maxle=500;ub=4;cr=21;crs=1;crv=0"));
			items.addAll(getIdsFromWowhead("http://"+wowheadPrefix+".wowhead.com/items?filter=qu=4;minle=501;maxle=510;ub=4;cr=21;crs=1;crv=0"));
			items.addAll(getIdsFromWowhead("http://"+wowheadPrefix+".wowhead.com/items?filter=qu=4;minle=511;maxle=530;ub=4;cr=21;crs=1;crv=0"));
			items.addAll(getIdsFromWowhead("http://"+wowheadPrefix+".wowhead.com/items?filter=qu=4;minle=531;maxle=550;ub=4;cr=21;crs=1;crv=0"));
			items.addAll(getIdsFromWowhead("http://"+wowheadPrefix+".wowhead.com/items?filter=qu=4;minle=551;maxle=580;ub=4;cr=21;crs=1;crv=0"));
			
			items.addAll(getIdsFromWowhead("http://"+wowheadPrefix+".wowhead.com/items?filter=qu=3;minle=430;maxle=500;ub=4;cr=21;crs=1;crv=0"));
			items.addAll(getIdsFromWowhead("http://"+wowheadPrefix+".wowhead.com/items?filter=qu=3;minle=501;maxle=550;ub=4;cr=21;crs=1;crv=0"));
			
			// monk
//			items.addAll(getIdsFromWowhead("http://"+wowheadPrefix+".wowhead.com/items?filter=qu=4;minle=430;maxle=483;ub=10;cr=21;crs=1;crv=0"));
//			items.addAll(getIdsFromWowhead("http://"+wowheadPrefix+".wowhead.com/items?filter=qu=4;minle=484;maxle=500;ub=10;cr=21;crs=1;crv=0"));
//			items.addAll(getIdsFromWowhead("http://"+wowheadPrefix+".wowhead.com/items?filter=qu=4;minle=501;maxle=600;ub=10;cr=21;crs=1;crv=0"));
//			
//			items.addAll(getIdsFromWowhead("http://"+wowheadPrefix+".wowhead.com/items?filter=qu=3;minle=430;maxle=500;ub=10;cr=21;crs=1;crv=0"));
//			items.addAll(getIdsFromWowhead("http://"+wowheadPrefix+".wowhead.com/items?filter=qu=3;minle=501;maxle=550;ub=10;cr=21;crs=1;crv=0"));
//			
//			// monk 2 handed weapons
//			items.addAll(getIdsFromWowhead("http://"+wowheadPrefix+".wowhead.com/items=2.10?filter=qu=3:4;minle=410;ub=10;cr=21;crs=1;crv=0"));
//			items.addAll(getIdsFromWowhead("http://"+wowheadPrefix+".wowhead.com/items=2.6?filter=qu=3:4;minle=410;ub=10;cr=21;crs=1;crv=0"));
			
			System.out.println("Importing " + items.size() + " items");
			
			for(Integer id : items){
				System.out.print(id + ",");
			}
			
			for(Integer id : items){
				itemDB.get(id);
			}
			
			// trinkets mop
			
			// 5.4
			Set<Integer> trinkets54 = new HashSet<Integer>();
			trinkets54.addAll(Arrays.asList(105029, 104780, 102301, 105278, 104531, 105527 )); // haromms_talisman
			trinkets54.addAll(Arrays.asList(105082, 104833, 102302, 105331, 104584, 105580 )); // sigil_of_rampage    
			trinkets54.addAll(Arrays.asList(104974, 104725, 102292, 105223, 104476, 105472 )); // assurance_of_consequence
			trinkets54.addAll(Arrays.asList(105114, 104865, 102311, 105363, 104616, 105612 )); // ticking_ebon_detonator
			trinkets54.addAll(Arrays.asList(103686, 103986 )); // discipline of xuen timeless isle trinkets
			for(Integer id : trinkets54){
				itemDB.get(id);
			}
			
			// 5.2
			itemDB.get(94511); // shadopan talisman
			itemDB.get(95625); // lfr Renataki's Soul Charm
			itemDB.get(94512); // normal Rena
			itemDB.get(95997); // thunder Rena
			itemDB.get(96369); // heroic Rena
			itemDB.get(96741); // heroic thunder
			itemDB.get(95748); // Talisman of Bloodlust lfr
			itemDB.get(94522); // normal
			itemDB.get(96492); // heroic
			itemDB.get(95665); // bad juju lfr
			itemDB.get(94523); // normal
			itemDB.get(96037); // thunder
			itemDB.get(96409); // heroic
			itemDB.get(96781); // heroic thunder
			itemDB.get(95802); // rune of re-origination lfr
			itemDB.get(96174); // thunder
			itemDB.get(94532); // normal
			itemDB.get(96546); // heroic
			itemDB.get(96918); // heroic thunder
			
			// 5.0 and 5.1
			itemDB.get(87057); // bottle of infinite stars
			itemDB.get(86132);
			itemDB.get(86791);
			itemDB.get(87574);
			itemDB.get(81265);
			itemDB.get(81267);
			itemDB.get(75274);
			itemDB.get(87495);
			
			itemDB.get(77534);
			
			itemDB.get(77530); // eng trinket: Ghost Iron Dragonling
			
			itemDB.get(73643); // old gladi insigna
			
			/*items = new HashSet<Integer>();
			items.addAll(getIdsFromWowhead("http://"+wowheadPrefix+".wowhead.com/search?q=nine+tailed"));
			for(Integer id : items){
				System.out.print(id+",");
			}
			System.out.println();*/
			
		}
	}
	
	private Set<Integer> getIdsFromWowhead(String url1){
		String url = loadURL(url1);
		Pattern pattern = Pattern.compile("\\_\\[(\\d*)\\]=\\{.*?\\}");
		Matcher matcher = pattern.matcher(url);
		Set<Integer> ret = new HashSet<Integer>();
		while (matcher.find()) {
			String s = matcher.group(1);
			int i = Integer.parseInt(s);
			ret.add(i);
		}
		return ret;
	}
	
	private void populateGems(String url2){
		 String url = loadURL(url2);
		 Pattern pattern = Pattern.compile("\\_\\[(\\d*)\\]=\\{.*?\\}");
		 Matcher matcher = pattern.matcher(url);
		 while (matcher.find()) {
			 String s = matcher.group(1);
			 int i = Integer.parseInt(s);
			 gemDB.getGem(i);
		 }
	}
	
	public ItemDB getDatabase(){
		return itemDB;
	}

	public GemDB getGemDB() {
		return gemDB;
	}

	public EnchantDB getEnchantDB() {
		return enchantDB;
	}

	public void save(Equipment equipment) {
		JSONObject js;
		long time = System.currentTimeMillis() / 1000;
		File file = new File("./db/" + time + ".character");
		js = new JSONObject(equipment);
		
		try {
			PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(file)));
			out.write(js.toString());
			out.flush();
			out.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public String[] getProfiles() {
		List<String> profiles = new ArrayList<String>();
		File file = new File("./db/");
		if(file.exists()){
			System.out.println("Path /db/ exists");
			File[] files = file.listFiles();
			for(int i = 0;i< files.length;i++){
				if(files[i].isFile()){
					profiles.add(files[i].getName());
				}
			}
		}
		return (String[])profiles.toArray(new String[profiles.size()]);
	}
}
